﻿using ImGuiNET;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using TTEditor;
using TTEditor.Project;
using TTEngine;

namespace FakeUnity.Editor
{
    class AppProxy
    {

    }
    class CustomLoader : AssemblyLoadContext
    {
        protected override Assembly Load(AssemblyName assemblyName)
        {
            return base.Load(assemblyName);
        }
    }
    static class Editor
    {
        static bool bInited = false;
        public static TTEditor.Project.Project curProject;
        static AssemblyLoadContext asseditor = null;
        static Warp.FrameworkNative.Texture ctex;
        //static TTEditor.EditorWindowMgr winmgr;
        public static void Run()
        {
            FakeUnity.Warp.FrameworkNative.App_MainLoop("c# driven imgui", OnInit, OnUpdate, OnGUI, OnExit);
        }
        static bool bShowDemo = false;
        static bool bShowFont = false;
        public static ILogger logger
        {
            get;
            private set;
        }

        class myalc : AssemblyLoadContext
        {
            public myalc() : base(true)
            {

            }
            protected override Assembly Load(AssemblyName assemblyName)
            {
                return null;// base.Load(assemblyName);
            }
        }
        static Queue<Action> mainthreadAction = new Queue<Action>();
        static void OnInit()
        {
            //初始化中文字体
            InitChineseFont();
            //初始化UI
            InitUI();
            var timport = new TTEngine.AssetExt.Texture2DConverter();
            TTEditor.Resource.ConverterMgr.AddResourceConverter(timport);

            var texview = new TTEngine.AssetExt.Texture2DViewer();
            TTEditor.Resource.ViewerMgr.RegViewer(texview);
        }
        static void InitChineseFont()
        {

            //異步加載中文字體，加載好了換上去。
            //這個操作不安定，需要換一個 fontatlas
            System.Threading.ThreadPool.QueueUserWorkItem((ss) =>
            {
                unsafe
                {
                    var io = ImGui.GetIO();
                    var t0 = DateTime.Now;
                    var fontatlas = ImGuiNET.ImGuiNative.ImFontAtlas_ImFontAtlas();
                    ImFontAtlasPtr Fonts = fontatlas;
                    var filename = "../resources/fonts/wqy-zenhei.ttc";
                    var font = Fonts.AddFontFromFileTTF(filename, 20.0f, null, io.Fonts.GetGlyphRangesChineseFull());
                    //不曉得這個是不是綫程安全的(明顯不安全),所以在一個新的fontatlas上搞
                    unsafe
                    {
                        Fonts.GetTexDataAsAlpha8(out byte* op, out int w, out int h);
                    }
                    //修改成 add的font暫時不可用，update之後才可以用
                    var t1 = DateTime.Now;
                    string outlog = "加载中文字体wqy-zenhei = " + (t1 - t0).TotalSeconds + "秒";

                    mainthreadAction.Enqueue(() =>
                    {

                        var oldfont = io.Fonts;

                        io.Fonts = Fonts;//切換字體集
                        Warp.FrameworkNative.App_UpdateFontTex();
                        io.FontDefault = font;

                        //卸載掉舊的font
                        ImGuiNET.ImGuiNative.ImFontAtlas_destroy(oldfont.NativePtr);
                        bInited = true;
                        if (logger != null)
                        {
                            logger.Log(outlog);
                        }
                    });
                }
            });

        }
        static void InitUI()
        {
            //init logo
            ctex = Warp.FrameworkNative.Texture_LoadFromFile("resources/textures/man01.png");
            //CreateWindow 可以开放给User

            TTEditor.EditorWindowMgr.AddWindow("Output", "输出Output", new WindowOutput());
            logger = TTEditor.EditorWindowMgr.GetWindowInfoByID("Output").win as ILogger;

            TTEditor.EditorWindowMgr.AddWindow("Project", "项目 Project", new WindowProject());
            TTEditor.EditorWindowMgr.AddWindow("Hierarchy", "结构 Hierarchy", new WindowHierarchy());
            TTEditor.EditorWindowMgr.AddWindow("Scene", "场景 Scene", new WindowScene());
            TTEditor.EditorWindowMgr.AddWindow("Property", "属性 Property", new WindowProperty());
            TTEditor.EditorWindowMgr.AddWindow("About", "关于 About", new WindowAbout(), false, false, false);
            CreateMenuEvent("File/OpenProject", File_OpenProj);
            CreateMenuEvent("File/CreateProject", File_CreateProj);
            CreateMenuEvent("File/CloseProject", File_CloseProj);

            CreateMenuEvent("Script/Build", Script_Build);
            //CreateMenuGUI是插入一个IMGUI函数，可以自由扩展，不会开放给User
            CreateMenuGUI("Windows/Views", OnMenuGUI_WindowsView);

            //CreateMenuEvent 是插入一个MenuItem，按下时仅处理事件，可以开放给User

            CreateMenuEvent("Help/About", () =>
            {
                TTEditor.EditorWindowMgr.ShowWindowByID("About");
            });

            CreateMenuEvent("Help/Test AppDomain", () =>
            {
                if (asseditor == null)
                {
                    asseditor = new myalc();
                    var path = typeof(Editor).Assembly.Location;
                    var dir = System.IO.Path.GetDirectoryName(path);
                    var wantdll = System.IO.Path.Combine(dir, "plugin.sample.dll");

                    using (var fs = System.IO.File.OpenRead(wantdll))
                    {
                        asseditor.LoadFromStream(fs);
                    }

                    foreach (var ass in asseditor.Assemblies)
                    {
                        foreach (var t in ass.GetTypes())
                        {
                            var allname = t.FullName;
                            allname = "";
                        }
                        var c1 = ass.GetType("plugin.sample.Class1");
                        var ctor = c1.GetConstructor(new Type[0]);
                        var ci1 = ctor.Invoke(null);


                    }

                    asseditor.Unload();
                    asseditor = null;

                    GC.Collect();
                    GC.WaitForFullGCComplete();
                }
            });
        }
        static void DrawLogo()
        {
            //创建一个全屏的Panel

            var viewport = ImGui.GetMainViewport();
            ImGui.SetNextWindowPos(viewport.Pos);
            ImGui.SetNextWindowSize(viewport.Size);
            ImGui.SetNextWindowViewport(viewport.ID);
            ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 0.0f);
            ImGui.PushStyleVar(ImGuiStyleVar.WindowBorderSize, 0.0f);
            ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, System.Numerics.Vector2.Zero);
            ImGuiWindowFlags window_flags = ImGuiWindowFlags.MenuBar | ImGuiWindowFlags.NoDocking;
            window_flags |= ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
            window_flags |= ImGuiWindowFlags.NoBringToFrontOnFocus | ImGuiWindowFlags.NoNavFocus;

            if (ImGui.Begin("LogoWindow", window_flags))
            {

                {
                    ImGui.Text("Init...");
                    ImGui.Text("Loading Chinese Font...(slow for now,will be improved later.)");
                    ImGui.Image(Warp.FrameworkNative.Texture_GetImGuiTexID(ctex), new System.Numerics.Vector2(256, 256));
                }

                ImGui.PopStyleVar();
                ImGui.PopStyleVar();
                ImGui.PopStyleVar();



                ImGui.End();
            }

        }
        static void DrawRootEditorWin()
        {
            //创建一个全屏的DockPanel
            var viewport = ImGui.GetMainViewport();
            ImGui.SetNextWindowPos(viewport.Pos);
            ImGui.SetNextWindowSize(viewport.Size);
            ImGui.SetNextWindowViewport(viewport.ID);
            ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 0.0f);
            ImGui.PushStyleVar(ImGuiStyleVar.WindowBorderSize, 0.0f);
            ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, System.Numerics.Vector2.Zero);
            ImGuiWindowFlags window_flags = ImGuiWindowFlags.MenuBar | ImGuiWindowFlags.NoDocking;
            window_flags |= ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
            window_flags |= ImGuiWindowFlags.NoBringToFrontOnFocus | ImGuiWindowFlags.NoNavFocus;

            if (ImGui.Begin("DockWindow", window_flags))
            {
                uint rootdockid = ImGui.GetID("rootdock");
                ImGui.DockSpace(rootdockid, System.Numerics.Vector2.Zero, ImGuiDockNodeFlags.PassthruCentralNode);
                ImGui.PopStyleVar();
                ImGui.PopStyleVar();
                ImGui.PopStyleVar();



                ImGui.End();
            }
        }
        static void OnMenuGUI_WindowsView()
        {
            foreach (var win in EditorWindowMgr.GetAllWindowInfos())
            {
                bool select = win.show;
                if (win.onmenu && ImGui.MenuItem(win.title, "", ref select))
                {
                    if (select != win.show)
                    {
                        if (select)
                        {
                            win.win.Show();
                        }
                        else
                        {
                            win.win.OnClose();
                        }
                    }
                }
            }
        }
        static void OnUpdate()
        {
            foreach (var win in TTEditor.EditorWindowMgr.GetAllWindowInfos())
            {
                if (win.show)
                    win.win.OnUpdate();
            }
            while (mainthreadAction.Count > 0)
            {
                mainthreadAction.Dequeue()();
            }
        }
        static void OnGUI()
        {

            if (!bInited)
            {
                DrawLogo();
            }
            else
            {
                DrawRootEditorWin();

                //simple window system
                foreach (var win in TTEditor.EditorWindowMgr.GetAllWindowInfos())
                {
                    if (win.show)
                    {
                        bool open = true;
                        ImGuiWindowFlags flag = ImGuiWindowFlags.NoCollapse;
                        if (!win.candock) flag |= ImGuiWindowFlags.NoDocking;
                        if (ImGui.Begin(win.title, ref open, flag))
                        {
                            if (open == false)
                            {
                                ImGui.End();
                                TTEditor.EditorWindowMgr.CloseWindowByID(win.ID);
                                continue;
                            }
                            win.win.OnGUI();
                        }
                        ImGui.End();

                    }
                }
                //simple menu system

                if (ImGui.BeginMainMenuBar())
                {
                    foreach (var m in menus)
                    {
                        m.Value.OnGUI(m.Key);
                    }


                    //this is for demo
                    if (ImGui.BeginMenu("Demo"))
                    {
                        ImGui.MenuItem("show demo", "", ref bShowDemo);
                        ImGui.MenuItem("show font", "", ref bShowFont);

                        ImGui.EndMenu();
                    }
                    ImGui.EndMainMenuBar();
                }

                //this is for demo
                if (bShowDemo)
                {
                    ImGui.ShowDemoWindow();
                }
                if (bShowFont)
                {
                    ImGui.ShowFontSelector("");
                }
            }

        }
        static void OnExit()
        {

        }
        static Dictionary<string, MenuInfo> menus = new Dictionary<string, MenuInfo>();
        class MenuInfo
        {
            //public string title;
            public Action onMenuEvent;
            public Action onMenuGUI;
            public Dictionary<string, MenuInfo> menus;
            public void OnGUI(string title)
            {
                if (this.onMenuEvent != null)
                {
                    if (ImGui.MenuItem(title))
                    {
                        onMenuEvent();
                    }
                }
                else if (this.onMenuGUI != null)
                {
                    if (ImGui.BeginMenu(title))
                    {
                        onMenuGUI();

                        ImGui.EndMenu();
                    }
                }
                else if (this.menus != null)
                {
                    if (ImGui.BeginMenu(title))
                    {
                        foreach (var m in this.menus)
                        {
                            m.Value.OnGUI(m.Key);
                        }
                        ImGui.EndMenu();
                    }
                }
                else
                {

                }
            }
            public void CreteMenuEvent(string[] paths, int pathindex, Action onMenuEvent)
            {
                if (pathindex == paths.Length)
                {
                    this.onMenuEvent = onMenuEvent;
                }
                else
                {
                    var curkey = paths[pathindex];
                    if (menus == null)
                        menus = new Dictionary<string, MenuInfo>();
                    if (!menus.ContainsKey(curkey))
                    {
                        menus[curkey] = new MenuInfo();
                        //menus[curkey].title = curkey;
                    }
                    menus[curkey].CreteMenuEvent(paths, pathindex + 1, onMenuEvent);
                }
            }
            public void CreteMenuGUI(string[] paths, int pathindex, Action onMenuGUI)
            {
                if (pathindex == paths.Length)
                {
                    this.onMenuGUI = onMenuGUI;
                }
                else
                {
                    var curkey = paths[pathindex];
                    if (menus == null)
                        menus = new Dictionary<string, MenuInfo>();
                    if (!menus.ContainsKey(curkey))
                    {
                        menus[curkey] = new MenuInfo();
                        //menus[curkey].title = curkey;
                    }
                    menus[curkey].CreteMenuGUI(paths, pathindex + 1, onMenuGUI);
                }
            }
        }
        public static void CreateMenuEvent(string path, Action onMenuEvent)
        {
            var paths = path.Split('/');
            if (!menus.ContainsKey(paths[0]))
            {
                menus[paths[0]] = new MenuInfo();
            }
            menus[paths[0]].CreteMenuEvent(paths, 1, onMenuEvent);
        }
        static void CreateMenuGUI(string path, Action onMenuGUI)
        {
            var paths = path.Split('/');
            if (!menus.ContainsKey(paths[0]))
            {
                menus[paths[0]] = new MenuInfo();
            }
            menus[paths[0]].CreteMenuGUI(paths, 1, onMenuGUI);

        }
        static string GetLastOpenFolder()
        {

            var path = Config.Get("lastproject");
            if (path == null)
            {
                path = System.IO.Directory.GetCurrentDirectory();
            }
            return path;

        }
        static void SetLastOpenFolder(string path)
        {
            Config.Set("lastproject",path);
        }
        static void File_CreateProj()
        {
            var rt = fakeunity.editor.nativefiledialog_native.NFD_PickFolder(GetLastOpenFolder(), out string setpath);
            if (rt == fakeunity.editor.nativefiledialog_native.nfdresult.NFD_OKAY)
            {
                SetLastOpenFolder(setpath);
                //Create Project
                if (curProject == null)
                {
                    curProject = Project.Create(setpath);
                }
            }
        }
        static void File_OpenProj()
        {
            var rt = fakeunity.editor.nativefiledialog_native.NFD_PickFolder(GetLastOpenFolder(), out string setpath);
            if (rt == fakeunity.editor.nativefiledialog_native.nfdresult.NFD_OKAY)
            {
                SetLastOpenFolder(setpath);
                //Open😂 Project
                if (curProject == null)
                {
                    try
                    {
                        curProject = Project.Open(setpath);
                    }
                    catch (Exception err)
                    {
                        logger.Error("OpenProject fail:" + err.Message);
                    }
                }
            }
        }
        static void File_CloseProj()
        {
            if (curProject != null)
            {
                curProject.Close();
            }
            curProject = null;
        }
        static void Script_Build()
        {
            if (curProject != null)
            {
                curProject.Build(Editor.logger);
            }
        }
    }
}
